<?php

declare(strict_types=1);

namespace Zyucv7\Core\Database;

use PDO;
use PDOStatement;

/**
 * 数据库操作核心接口
 * 定义了所有数据库驱动必须实现的基本CRUD方法。
 */
abstract class Driver
{

    protected PDO $pdo;
    protected PDOStatement $stmt;
    protected $config;
    protected $options = [
        PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        PDO::ATTR_EMULATE_PREPARES   => false,
    ];
    private $lastSQL = '';

    public function getTableName(string $table, string $alias = ''): string
    {
        return ($this->config['prefix']??'') . $table.($alias ? ' '.$alias : ' '.$table);
    }

    private function getRealTableName(string $table): string
    {
        return ($this->config['prefix']??'') . $table;
    }

    /**
     * 添加单条记录
     * @param string $table 表名
     * @param array $data 数据数组 ['column' => 'value']
     * @return int 返回最后插入的ID
     */
    public function insert(string $table, array $data): int
    {
        $columns = implode(', ', array_keys($data));
        $placeholders = implode(', ', array_fill(0, count($data), '?'));
        $sql = "INSERT INTO {$this->getRealTableName($table)} ({$columns}) VALUES ({$placeholders})";
        $this->query($sql, array_values($data));
        return (int)$this->pdo->lastInsertId();
    }

    /**
     * 删除记录
     * @param string $table 表名
     * @param string $where WHERE条件, 例如 "id = ?"
     * @param array $params WHERE条件对应的参数值
     * @return int 返回受影响的行数
     */
    public function delete(string $table, string $where, array $params): int
    {
        $sql = "DELETE FROM {$this->getRealTableName($table)} WHERE {$where}";
        $this->query($sql, $params);
        return $this->stmt->rowCount();
    }

    /**
     * 修改记录
     * @param string $table 表名
     * @param array $data 要更新的数据数组 ['column' => 'value']
     * @param string $where WHERE条件, 例如 "id = ?"
     * @param array $params WHERE条件对应的参数值
     * @return int 返回受影响的行数
     */
    public function update(string $table, array $data, string $where, array $params): int
    {
        $set = [];
        foreach ($data as $column => $value) {
            $set[] = "$column = ?";
        }
        $sql = "UPDATE {$this->getRealTableName($table)} SET " . implode(', ', $set) . " WHERE {$where}";
        $this->query($sql, array_merge(array_values($data), $params));
        return $this->stmt->rowCount();
    }

    /**
     * 查询多条记录（集合）
     * @param string $table 表名
     * @param array $queryOptions 查询选项, 可选, 例如 ['order' => 'id DESC', 'limit' => 10, 'offset' => 0]
     * @param array $params WHERE条件对应的参数值, 可选
     * @return array 返回包含所有结果的二维数组
     */
    public function findAll(string $table, array $queryOptions = [], array $params = []): array
    {
        $queryOptions['table'] = $table;
        $sql = $this->getQuerySql($queryOptions);
        return $this->query($sql, $params)->fetchAll();
    }
    
    /**
     * 查询单条记录
     * @param string $table 表名
     * @param string $where WHERE条件, 例如 "id = ?"
     * @param array $params WHERE条件对应的参数值
     * @return array|null 返回包含单条记录的一维数组，未找到则返回null
     */
    public function findOne(string $table, array $queryOptions = [], array $params)
    {
        $queryOptions['table'] = $table;
        $queryOptions['limit'] = 1;
        $sql = $this->getQuerySql($queryOptions);
        return $this->query($sql, $params)->fetch();
    }

    /**
     * 查询单个值
     * @param string $table 表名
     * @param string $column 要查询的列名
     * @param string $where WHERE条件, 例如 "id = ?"
     * @param array $params WHERE条件对应的参数值
     * @return mixed 返回单个值（字符串、数字等），未找到则返回null
     */
    public function findValue(string $table, string $column, string $where, array $params)
    {
        $sql = "SELECT {$column} FROM {$this->getTableName($table)} ".($where ? "WHERE ".$where : "")." LIMIT 1";
        $this->query($sql, $params);
        return $this->stmt->fetchColumn();
    }

    protected function getQuerySql($queryOptions) {
        $orderBy = $queryOptions['order'] ?? '';
        $limit = $queryOptions['limit'] ?? '';
        $join = $queryOptions['join'] ?? '';
        $groupBy = $queryOptions['group'] ?? '';
        $table = $this->getTableName($queryOptions['table']);
        $where = $queryOptions['where'] ?? '';
        $fields = $queryOptions['fields'] ?? '*';
        $sql = "SELECT {$fields} FROM {$table}";
        if ($join) {
            $sql .= " {$join}";
        }
        if ($where) {
            $sql .= " WHERE {$where}";
        }
        if ($groupBy) {
            $sql .= " GROUP BY {$groupBy}";
        }
        if ($orderBy) {
            $sql .= " ORDER BY {$orderBy}";
        }
        if ($limit) {
            $sql .= " LIMIT {$limit}";
        }

        return $sql;
    }
    /**
     * 执行复杂查询SQL语句
     * @param string $sql 完整的SQL查询语句
     * @param array $params SQL语句中的参数值
     * @return PDOStatement 返回当前数据库连接的 PDOStatement 实例
     */
    public function query(string $sql, array $params = []): self
    {
        $this->lastSQL = $this->getFullSQL($sql, $params);
        try {
            $pdo = $this->getPdo();
            $this->stmt = $pdo->prepare($sql);
            $this->stmt->execute($params);
        } catch (\PDOException $e) {
            throw DatabaseException::Pdo($e->getMessage().V7_EOL."SQL:".$this->lastSQL);
        }
        return $this;
    }

    /**
     * 获取所有查询结果
     * @return array 返回包含所有结果的二维数组
     */
    public function fetchAll(): array
    {
        return $this->stmt->fetchAll();
    }

    /**
     * 获取单条查询结果
     * @return mixed 返回单条结果的一维数组，未找到则返回null
     */
    public function fetch()
    {
        $result = $this->stmt->fetch();
        return $result === false ? null : $result;
    }

    /**
     * 获取底层的 PDO 实例
     * @return PDO 返回当前数据库连接的 PDO 实例
     */
    public function getPdo(): PDO
    {
        return $this->pdo;
    }

    /**
     * 将SQL语句中的问号占位符替换为实际参数值（用于调试）
     * 
     * @param string $sql 带有问号占位符的SQL语句
     * @param array $params 参数值数组
     * @return string 替换后的SQL语句（已转义参数值）
     */
    private function getFullSQL(string $sql, array $params) {
        $parts = explode('?', $sql);
        $result = '';
        $paramIndex = 0;
        $paramCount = count($parts) - 1;
        
        // 检查参数数量是否匹配
        if ($paramCount != count($params)) {
            throw DatabaseException::InvalidArgument("参数数量不匹配: SQL中有 {$paramCount} 个占位符，但提供了 " . count($params) . " 个参数");
        }
        
        foreach ($parts as $i => $part) {
            $result .= $part;
            
            // 如果不是最后一个部分且还有参数，则添加参数
            if ($i < $paramCount) {
                $param = $params[$paramIndex++];
                // 字符串类型 - 转义单引号
                $escaped = str_replace("'", "''", $param);
                $result .= "'{$escaped}'";
            }
        }
        
        return $result;
    }

    /**
     * 获取完整的SQL
     * 
     * @return string
     */
    public function getRawSQL() {
        return $this->lastSQL;
    }
}
